package com.zx;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.*;
import java.util.*;
import java.util.function.Function;

public class PropertyResolver {

    Logger logger = LoggerFactory.getLogger(getClass());
    //用于存储配置文件中的配置，这里存储的配置值是String类型
    //代码中提供的getProperty方法中，可以将配置值转换成对应类型
    Map<String, String> properties = new HashMap<>();
    //类型转换器集合，存储了很多类型转换方法，都是将String类型转换成其他类型
    Map<Class<?>, Function<String, Object>> converters = new HashMap<>();

    public PropertyResolver(Properties props) {
        //将系统参数放到map中
        this.properties.putAll(System.getenv());
        Set<String> names = props.stringPropertyNames();
        for (String name : names) {
            this.properties.put(name, props.getProperty(name));
        }

        //将类型转换方法放到map中
        converters.put(String.class, s -> s);
        converters.put(boolean.class, s -> Boolean.parseBoolean(s));
        converters.put(Boolean.class, s -> Boolean.valueOf(s));

        converters.put(byte.class, s -> Byte.parseByte(s));
        converters.put(Byte.class, s -> Byte.valueOf(s));

        converters.put(short.class, s -> Short.parseShort(s));
        converters.put(Short.class, s -> Short.valueOf(s));

        converters.put(int.class, s -> Integer.parseInt(s));
        converters.put(Integer.class, s -> Integer.valueOf(s));

        converters.put(long.class, s -> Long.parseLong(s));
        converters.put(Long.class, s -> Long.valueOf(s));

        converters.put(float.class, s -> Float.parseFloat(s));
        converters.put(Float.class, s -> Float.valueOf(s));

        converters.put(double.class, s -> Double.parseDouble(s));
        converters.put(Double.class, s -> Double.valueOf(s));

        converters.put(LocalDate.class, s -> LocalDate.parse(s));
        converters.put(LocalTime.class, s -> LocalTime.parse(s));
        converters.put(LocalDateTime.class, s -> LocalDateTime.parse(s));
        converters.put(ZonedDateTime.class, s -> ZonedDateTime.parse(s));
        converters.put(Duration.class, s -> Duration.parse(s));
        converters.put(ZoneId.class, s -> ZoneId.of(s));
    }

    public String getProperty(String key) {
        //由于传进来的key可能是${key:defaultValue}格式，所以需要parsePropertyExpr方法进行解析
        PropertyExpr keyExpr = parsePropertyExpr(key);
        //parsePropertyExpr方法返回值不为null，说明key是${key:defaultValue}这种格式
        if (keyExpr != null) {
            if (keyExpr.getDefaultValue() == null) {
                //进入这里说明key的格式是${key}
                return getRequiredProperty(keyExpr.getKey());
            } else {
                //进入这里说明key的格式是${key:defaultValue}
                return getProperty(keyExpr.getKey(), keyExpr.getDefaultValue());
            }
        }
        //代码走到这里，说明key不是${key:defaultValue}格式
        String value = this.properties.get(key);
        if (value != null) {
            //由于yaml文件中的配置值可能是${key1:${key2:defaultValue}}格式，所以需要parseValue方法进行解析
            return parseValue(value);
        }
        return value;
    }

    public String getProperty(String key, String defaultValue) {
        String value = getProperty(key);
        //如果根据key在map中获取不到value值，就继续解析value
        //由于value可能是嵌套key格式：${key1:${key2:defaultValue}}，所以调用parseValue方法解析
        return value == null ? parseValue(defaultValue) : value;
    }

    public <T> T getProperty(String key, Class<T> targetType) {
        String value = getProperty(key);
        if (value == null) {
            return null;
        }
        return convert(targetType, value);
    }

    public <T> T getProperty(String key, Class<T> targetType, T defaultValue) {
        String value = getProperty(key);
        if (value == null) {
            return defaultValue;
        }
        return convert(targetType, value);
    }

    public PropertyExpr parsePropertyExpr(String key) {
        //如果key是${key}，${key:defaultValue}
        if (key.startsWith("${") && key.endsWith("}")) {
            int n = key.indexOf(":");
            if (n == -1) {
                //进入这里说明key的格式是${key}
                String k = notEmpty(key.substring(2, key.length() - 1));
                return new PropertyExpr(k, null);
            } else {
                //进入这里说明key的格式是${key:defaultValue}
                String k = notEmpty(key.substring(2, n));
                return new PropertyExpr(k, key.substring(n+1, key.length()-1));
            }
        }
        //执行到这里说明key就是普通的格式
        return null;
    }

    public String notEmpty (String s) {
        if (s.isEmpty()) {
            throw new IllegalArgumentException("Invalid key: " + s);
        }
        return s;
    }

    public String getRequiredProperty(String s) {
        String value = getProperty(s);
        return Objects.requireNonNull(value, "Property '" + s + "' not found.");
    }

    public String parseValue (String value) {
        //由于value的格式可能是正常的配置值，也可能是嵌套key格式：${key1:${key2:defaultValue}}，需要parsePropertyExpr方法解析
        PropertyExpr expr = parsePropertyExpr(value);
        if (expr == null) {
            //进入这里说明value是正常的配置值
            return value;
        }
        if (expr.getDefaultValue() != null) {
            //进入这里说明value是嵌套key格式：${key1:${key2:defaultValue}}
            return getProperty(expr.getKey(), expr.getDefaultValue());
        } else {
            //进入这里说明value是最后一层的key，需要根据key获取value
            return getRequiredProperty(expr.getKey());
        }
    }

    public <T> T convert (Class<?> clazz, String value) {
        Function<String, Object> fn = this.converters.get(clazz);
        if (fn == null) {
            throw new IllegalArgumentException("Unsupported value type: " + clazz.getName());
        }
        return (T) fn.apply(value);
    }

    class PropertyExpr {
        private String key;
        private String defaultValue;

        public PropertyExpr () {

        }

        public PropertyExpr(String key, String defaultValue) {
            this.key = key;
            this.defaultValue = defaultValue;
        }

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public String getDefaultValue() {
            return defaultValue;
        }

        public void setDefaultValue(String defaultValue) {
            this.defaultValue = defaultValue;
        }
    }
}
